/**
* Project Name:sjgl
* Date:2018年12月16日
* Copyright (c) 2018, jingma All Rights Reserved.
*/

package cn.benma666.sjsj.ljq.sjgl;

import cn.benma666.dict.Ljpd;
import cn.benma666.dict.ZdKjlx;
import cn.benma666.domain.SysSjglFile;
import cn.benma666.domain.SysSjglSjdx;
import cn.benma666.domain.SysSjglSjzd;
import cn.benma666.exception.DictException;
import cn.benma666.exception.MyException;
import cn.benma666.iframe.*;
import cn.benma666.myutils.DateUtil;
import cn.benma666.myutils.StringUtil;
import cn.benma666.sjsj.myutils.MyTransactional;
import cn.benma666.sjsj.web.DefaultLjq;
import cn.benma666.sjsj.web.LjqManager;
import cn.benma666.sjzt.Db;
import cn.benma666.sjzt.ES;
import cn.benma666.sjzt.MyLambdaQuery;
import cn.benma666.sjzt.SjztLx;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.stuxuhai.jpinyin.PinyinException;
import org.beetl.sql.core.SqlId;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;

/**
 * 数据对象拦截器 <br/>
 * date: 2018年12月16日 <br/>
 * @author jingma
 * @version 0.1
 */
@Component("SYS_SJGL_SJDX")
@Scope("prototype")
public class SjdxLjq extends DefaultLjq implements DisposableBean, InitializingBean {

    @Override
    public Result save(MyParams myParams) {
        Result r = super.save(myParams);
        CacheFactory.clear(KEY_SJDX);
        DictManager.clearDict("SYS_SJGL_SJDX");
        return r;
    }
    @Override
    @MyTransactional
    public Result insert(MyParams myParams) throws MyException {
        SysSjglSjdx jtdx = myParams.yobj(SysSjglSjdx.class);
        jtdx.setId(StringUtil.getUUIDUpperStr());
        //对象代码统一为大写
        jtdx.setDxdm(jtdx.getDxdm().toUpperCase());
        Result r = super.insert(myParams);
        if(!r.isStatus()){
            return r;
        }
        //新增
        try {
            r.addMsg(impFields(jtdx,myParams).getMsg());
        } catch (MyException e) {
            throw e;
        } catch (Exception e) {
            throw new MyException("导入字段出错",e);
        }
        clearCache();
        return r;
    }

    @Override
    public Result update(MyParams myParams) throws MyException {
        String dxdm = myParams.getString("$.yobj.dxdm");
        if(!isBlank(dxdm)){
            //对象代码统一为大写
            myParams.set("$.yobj.dxdm",dxdm.toUpperCase());
        }
        Result r = super.update(myParams);
        clearCache();
        return r;
    }

    @Override
    public Result plsc(MyParams myParams) {
        //删除相关字段
        Result result = super.plsc(myParams);
        //后进行逻辑删除
        int sczds = getDb().update(SqlId.of("sjsj","updateSjzd"),myParams);
        result.addMsg("逻辑删除字段数："+sczds);
        clearCache();
        return result;
    }
    public Result searchTableByEs(MyParams myParams) {
        try {
            String searchKey = myParams.getString("$.sys.searchKey");
            String sdm = myParams.getString("$.yobj.dm");
            Integer pageNumber = myParams.getInteger("$.page.pageNumber");
            Integer pageSize = myParams.getInteger("$.page.pageSize");
            // 获取索引列表
            List<JSONObject> array = ES.use(myParams.getString("$.yobj.sjzt")).getIndexList();
            // 过滤后的索引列表
            List<JSONObject> glArray;
            // 判断是否过滤
            if (!isBlank(sdm)) {
                glArray = array.stream().filter(i -> sdm.equals(i.getString("dm")))
                        .collect(Collectors.toList());
            } else if (!isBlank(searchKey)){
                glArray = array.stream().filter(i -> i.toJSONString().contains(searchKey))
                        .collect(Collectors.toList());
            } else {
                glArray = array;
            }
            PageInfo pageInfo = new PageInfo();
            List<JSONObject> list = new ArrayList<>();
            for (int i = 0; i < glArray.size(); i++) {
                if (i >= (pageNumber - 1) * pageSize && (i + 1) <= pageNumber * pageSize) {
                    JSONObject t = glArray.get(i);
                    String dm = t.getString("dm");
                    String mc = t.getString("mc");
                    JSONObject index = new JSONObject();
                    index.put("dm", dm);
                    index.put("mc", mc);
                    index.put("px", "");
                    index.put("search_key", "");
                    index.put("upnode", "");
                    if (StringUtil.isNotBlank(searchKey)) {
                        if (t.toJSONString().contains(searchKey)) {
                            list.add(index);
                        }
                        {
                            continue;
                        }
                    } else {
                        list.add(index);
                    }
                }
            }
            pageInfo.setTotalRow(glArray.size());
            pageInfo.setTotalPage(glArray.size() / pageSize);
            pageInfo.setPageNumber(pageNumber);
            pageInfo.setPageSize(pageSize);
            pageInfo.setList(list);
            return success("操作成功", pageInfo);
        } catch (Exception e) {
            throw new MyException("获取索引列表失败：" + e.getMessage());
        }
    }

    /**
     * @return 默认导入字段的sql
     */
    public Result dis(MyParams myParams) {
        SysSjglSjdx ysjdx = myParams.getObject(KEY_YOBJ, SysSjglSjdx.class);
        if(Db.isSupported(ysjdx.getDxztlx())){
//            myParams.put(KEY_YOBJ,ysjdx.toJSONObject());
            String[] arr = Db.parseDictExp(Db.getZdSqlTmpl(myParams,"DEFAULT.dis",
                    ysjdx.getDxztlx()), ysjdx.getDxzt());
            SysSjglSjdx dx = new SysSjglSjdx();
            assert arr != null;
            dx.setZddrsql(arr[1]);
            return success("获取字段默认导入sql成功", dx);
        }
        throw new MyException("该对象载体类型不支持获取导入字段sql："+ysjdx.getDxztlx());
    }

    /**
     * 复制对象
     */
    @MyTransactional
    public Result fzjl(MyParams myParams){
        SysSjglSjdx yobj = myParams.yobj(SysSjglSjdx.class);
        int count = 0;
        //复制对象
        String dmhz = "_"+DateUtil.getGabDate();
        String mchz = "-复制品";
        if(StringUtil.isNotBlank(yobj.getDmhz())){
            dmhz = "_"+yobj.getDmhz();
        }
        if(StringUtil.isNotBlank(yobj.getMchz())){
            mchz = "-"+yobj.getMchz();
        }
        List<SysSjglSjdx> list = ((PageInfo<JSONObject>) select(myParams).getData()).getList(SysSjglSjdx.class);
        for(SysSjglSjdx obj : list){
            String id = obj.getId();
            //获取对象
            obj.setId(StringUtil.getUUIDUpperStr());
            JSONObject kzxx = obj.getKzxxObj();
            if(isBlank(kzxx.getString($_SYS_FSJDXDM))){
                kzxx.set($_SYS_FSJDXDM,obj.getDxdm());
                obj.setKzxx(kzxx.toString(SerializerFeature.PrettyFormat));
            }
            obj.setDxdm(obj.getDxdm()+dmhz);
            obj.setDxmc(obj.getDxmc()+mchz);
            myParams.put(KEY_YOBJ,obj);
            super.insert(myParams);
            //复制字段
            myParams.yobj().set("oldSjdxId",id);
            myParams.yobj().set("newSjdx",obj);
            String[] arr = LjqManager.getSql(myParams, "fzzd");
            db(arr[0]).update(arr[1], myParams);
            count++;
        }
        return success("成功复制对象个数："+count);
    }

    /**
     * 刷新对象，主要是数据库中字段变化的场景
     */
    @MyTransactional
    public Result sxdx(MyParams myParams) {
        int count = 0;
        Result result =  success("");
        List<SysSjglSjdx> list = LjqManager.select(myParams).getPageList(SysSjglSjdx.class);
        for(SysSjglSjdx jtdx:list){
            //获取对象
            myParams.put(KEY_YOBJ,jtdx);
            Result r;
            try {
                r = impFields(jtdx,myParams);
            } catch (PinyinException e) {
                throw new MyException("导入字段出错："+e.getMessage(),e);
            }
            result.addMsg(r.getMsg());
            count++;
        }
        clearCache();
        result.addMsg("成功刷新对象个数："+count);
        return result;
    }

    /**
     * 批量标准排序，当排序较频繁时，序号密度较高后可以采用此方法进行重新分布
     */
    @MyTransactional
    public Result bzpx(MyParams myParams){
        //标准排序
        Result result = success("");
        int count = 0;
        for(String id:myParams.getJSONArray($_SYS_IDS).toJavaList(String.class)){
            MyParams jtdxParams = LjqManager.jcxxById(id);
            Result r = bzpxFields(jtdxParams);
            result.addMsg(r.getMsg());
            count++;
        }
        clearCache();
        result.addMsg("成功重新排序对象个数："+count);
        return result;
    }

    /**
     * 更新去重字段
     */
    @MyTransactional
    public Result gxqczd(MyParams myParams){
        //标准排序
        Result result = success("");
        int count = 0;
        for(String id:myParams.getJSONArray($_SYS_IDS).toJavaList(String.class)){
            MyParams jtdxParams = LjqManager.jcxxById(id);
            Result r = gxqczdFields(jtdxParams);
            result.addMsg(r.getMsg());
            count++;
        }
        clearCache();
        result.addMsg("成功更新对象个数："+count);
        return result;
    }

    private Result gxqczdFields(MyParams myParams) {
        SysSjglSjdx sjdx1 = myParams.getObject(KEY_SJDX, SysSjglSjdx.class);
        Map<String ,SysSjglSjzd> qcList = (Map<String ,SysSjglSjzd>) fieldsCache.get(sjdx1.getId()+ QCZD_LIST);
        if(qcList.size()==1){
            return success("只有一个去重字段，不需要更新去重字段");
        }else if(StringUtil.isNotBlank(sjdx1.getQczd())){
            //多个去重字段，且设置了去重字段
            String[] sql = LjqManager.getSql(myParams,"gxqczd");
            return success(sjdx1.getDxmc()+"去重字段更新成功"+db(sql[0]).update(sql[1],myParams));
        }else{
            return failed(myParams.getString("$.sjdx.dxmc")+"配置了具体的多个去重字段，但没有在数据对象中配置存储去重字段md5值的“去重字段”");
        }
    }

    /**
     * 下载实体脚本
     */
    public Result xzstjb(MyParams myParams) {
        String sjzt = null;
        StringBuilder data = new StringBuilder();
        String cs;
        Set<String> jtdxSet = new HashSet<>();
        for(String id:myParams.getJSONArray($_SYS_IDS).toJavaList(String.class)){
            JSONObject jtdxParams = LjqManager.jcxxById(id);
            if(jtdxSet.contains(jtdxParams.getString("$.sjdx.jtdx"))||"xndx".equals(jtdxParams.getString("$.sjdx.dxlx"))){
                //以jtdx为依据去重或虚拟对象
                continue;
            }
            jtdxSet.add(jtdxParams.getString("$.sjdx.jtdx"));
            jtdxParams.put(KEY_YOBJ,myParams.get(KEY_YOBJ));
            String dxztlx = jtdxParams.getString("$.sjdx.dxztlx");
            if(Db.isSupported(dxztlx)){
                //生成表
                sjzt = valByDef(myParams.getString("$.yobj.sjzt"),jtdxParams.getString("$.sjdx.dxzt"));
                cs = db(sjzt).getSourceSql(SqlId.of("util","createTable"),jtdxParams);
                data.append(cs).append(";\n");
            }else {
                throw new MyException("不支持的对象载体类型："+dxztlx);
            }
        }
        SysSjglFile file1 = new SysSjglFile();
        file1.setWjlx("sql");
        file1.setWjm(sjzt+"实体脚本-"+DateUtil.getGabDate()+".sql");
        return resultFile(data.toString().getBytes(StandardCharsets.UTF_8),file1);
    }
    /**
     * 生成对象实体，就是根据对象配置生成数据库表等
     */
    @MyTransactional
    public Result scdxst(MyParams myParams) {
        int count = 0;
        String sjzt;
        String cs;
        for(String id:myParams.getJSONArray($_SYS_IDS).toJavaList(String.class)){
            JSONObject jtdxParams = LjqManager.jcxxById(id);
            jtdxParams.put(KEY_YOBJ,myParams.get(KEY_YOBJ));
            String dxztlx = jtdxParams.getString("$.sjdx.dxztlx");
            if(Db.isSupported(dxztlx)){
                //生成表
                sjzt = valByDef(myParams.getString("$.yobj.sjzt"),jtdxParams.getString("$.sjdx.dxzt"));
                cs = db(sjzt).getSourceSql(SqlId.of("util","createTable"),jtdxParams);
                for(String sql:cs.split(";--")){
                    db(sjzt).update(sql);
                }
            }else {
                throw new MyException("不支持的对象载体类型："+dxztlx);
            }
            count++;
        }
        return success("成功生成对象个数："+count);
    }
    /**
    * 单个对象标准排序 <br/>
    * @author jingma
    * @param myParams 相关参数
    * @return 处理结果
    */
    protected Result bzpxFields(MyParams myParams) {
        Map<String, SysSjglSjzd> fields = (Map<String, SysSjglSjzd>) myParams.get(KEY_FIELDS);
        int idx = 50;
        for(Entry<String, SysSjglSjzd> field:fields.entrySet()){
            idx+=10;
            if(isBlank(field.getValue().getId())){
                continue;
            }
            final MyLambdaQuery<SysSjglSjzd> sjzdMyLambdaQuery = getDb().lambdaQuery(SysSjglSjzd.class,
                    myParams.user());
            sjzdMyLambdaQuery.andEq(SysSjglSjzd::getId,field.getValue().getId())
                    .updateSelective(SysSjglSjzd.builder().px(idx).build());
        }
        clearCache();
        return success(myParams.getString("$.sjdx.dxmc")+"成功标准化排序字段数："+fields.size());
    }

    /**
     * 物理删除-基于有效性
     */
    @Override
    @MyTransactional
    protected Result wlscByYxx(MyParams myParams) {
        Result result = super.wlscByYxx(myParams);
        int scs = getDb().update(SqlId.of("sjsj","deleteSjzd"), myParams);
        result.addMsg("物理删除字段数："+scs);
        return result;
    }
    /**
    * 导入字段 <br/>
    * @author jingma
    * @param jtdx 要操作的具体对象
    * @param myParams 相关参数
    * @return 处理结果
    */
    protected Result impFields(SysSjglSjdx jtdx, MyParams myParams) throws PinyinException {
        if(Db.isSupported(jtdx.getDxztlx())){
            return impFieldsDB(jtdx,myParams);
        }
        try {
            SjztLx lx = SjztLx.valueOf(jtdx.getDxztlx());
            switch (lx) {
                case ftp:
                case bdwj:
                    return impFieldsBdwj(jtdx,myParams);
                case es:
                    return crzd(jtdx, myParams, ES.use(jtdx.getDxzt()).getFields(jtdx.getJtdx()));
            }
        }catch (IllegalArgumentException e){
            //忽略
        }
        throw new MyException("不支持的对象载体类型："+jtdx.getDxztlx());
    }

    /**
     * 导入文件字段 <br/>
     * @author jingma
     * @param jtdx 要操作的具体对象
     * @param myParams 相关参数
     * @return 处理结果
    */
    protected Result impFieldsBdwj(SysSjglSjdx jtdx, MyParams myParams) {
        if(isBlank(jtdx.getZddrsql())){
            //文件类载体，无法自动获取字段，若没有手动配置则跳过
            return success("没有导入字段");
        }
        //非查询语句则按自定义字段规则导入。
        List<JSONObject> fieldsList = new ArrayList<>();
        for(String row:jtdx.getZddrsql().split("\n")){
            JSONObject zdObj = new JSONObject();
            String[] sxs = row.split("\\|");
            if(sxs.length!=4){
                return failed("非自动导出字段场景，自定义字段没有按指定规则填写："+row);
            }
            zdObj.put("zddm", sxs[0]);
            zdObj.put("zdms", sxs[1]);
            zdObj.put("zdlx", sxs[2]);
            zdObj.put("zdcd", sxs[3]);
            fieldsList.add(zdObj);
        }
        return crzd(jtdx, myParams, fieldsList);
    }
    /**
     * 获取默认导入sql-载体类型为数据库 <br/>
     * @author jingma
     * @param jtdx 要操作的具体对象
     * @param myParams 相关参数
     * @return 处理结果
    */
    protected Result impFieldsDB(SysSjglSjdx jtdx, MyParams myParams) {
        String zddrsql = jtdx.getZddrsql();
        if(isBlank(zddrsql)){
            Result r = dis(myParams);
            if(!r.isStatus()){
                //获取导入字段sql失败
                return r;
            }
            zddrsql = r.getData(SysSjglSjdx.class).getZddrsql();
        }
        List<JSONObject> fieldsList = db(jtdx.getDxzt()).find(zddrsql,myParams);
        return crzd(jtdx, myParams, fieldsList);
    }
    /**
     * 插入字段 <br/>
     * @author jingma
     * @param jtdx 要操作的具体对象
     * @param myParams 相关参数
     * @return 处理结果
    */
    protected Result crzd(SysSjglSjdx jtdx, MyParams myParams,
            List<JSONObject> fieldsList) {
        Map<String, JSONObject> oldFiledMap = getDb().findMap("zddm",
                "select * from sys_sjgl_sjzd t where t.sjdx=?",jtdx.getId());
        //新导入的对象复制默认字段
        int count = 0;
        if(oldFiledMap.isEmpty()&&valByDef(myParams.getBoolean("yobj.fzzd"),true)){
            //没有存在的字段且要求复制字段
            myParams.yobj().set("newSjdx",jtdx);
            myParams.yobj().set("newSjzd",fieldsList);
            //新建对象
            myParams.yobj().set("xjdx", true);
            String[] arr = LjqManager.getSql(myParams, "fzzd");
            count = getDb().update(arr[1], myParams);
            //字段复制后，重新读取字段
            oldFiledMap = getDb().findMap("zddm",
                    "select * from sys_sjgl_sjzd t where t.sjdx=?",jtdx.getId());
        }
        MyParams zdParams = LjqManager.jcxxByDxdm("SYS_SJGL_SJZD");
        int idx = oldFiledMap.size()*10+50;
        for(JSONObject fieldObj:fieldsList){
            idx += 10;
            //设置原始字段代码，不自动设置，默认采用字段代码
//            fieldObj.put("yszddm",fieldObj.getString("zddm").toLowerCase());
            //本系统统一，数据库中带下划线，本系统采用驼峰命名字段，实体类、数据库查询结果、数据对象中的字段统一
            String zddm = StringUtil.underlineTohump(fieldObj.getString("zddm"));
//            String zddm = fieldObj.getString("zddm").trim().toLowerCase();
            //设置字段代码
            fieldObj.put("zddm", zddm);
            fieldObj.put("zdlx",DictManager.zdMcByDm("SYS_SJGL_ZDLX_"+jtdx.getDxztlx().toUpperCase(),fieldObj.getString("zdlx")));
            if(oldFiledMap.containsKey(zddm)){
                //存在的字段
                continue;
            }
            count++;
            //设置对应的数据对象关联信息
            fieldObj.put("sjdx",jtdx.getId());
            //根据数据库顺序进行设置px作为默认排序
            fieldObj.put("px",idx);
            if(StringUtil.isNotBlank(fieldObj.getString("zdms"))){
                //根据字段描述进行默认设置
                String[] zdms = fieldObj.getString("zdms").trim().replace("；", ";").split(";");
                String[] zdms1 = zdms[0].split("@");
                //设置字段名称
                fieldObj.put("zdmc",isBlank(zdms1[0])?zddm:zdms1[0].trim());
                if(zdms1.length==2&&!isBlank(zdms1[1])){
                    //逻辑判断类的字段一般都比较短
                    if("SYS_COMMON_LJPD".equals(zdms1[1])){
                        fieldObj.put("zdzdlb",zdms1[1]);
                        fieldObj.put("zdkd",80);
                        fieldObj.put("kjlx", ZdKjlx.Switch.getCode());
                    }else{
                        try {
                            DictManager.zdMap(zdms1[1]);
                            //字典类别存才设置控件类型为字典，避免默认场景报错
                            //自动进行字典的一些常见设置
                            fieldObj.put("zdzdlb",zdms1[1]);
                            fieldObj.put("kjlx",ZdKjlx.Select.getCode());
                        }catch (DictException e){
                            //忽略
                        }
                    }
                    fieldObj.put("zdms",zdms1[1]);
                }else if(zdms1[0].contains("时间") || zdms1[0].contains("日期")){
                    //时间字段的默认设置
                    fieldObj.put("kjlx",ZdKjlx.ElDatePicker.getCode());
                    fieldObj.put("zdkd",130);
                    if(StringUtil.isNotBlank(fieldObj.getString("xzmrz"))){
                        fieldObj.put("xzmrz","{'method':'add','dw':'day','value':'0'}");
                    }
                }
                if(zdms.length==2){
                    //存在单独的字段描述信息
                    fieldObj.put("zdms",zdms[1]);
                }
            }else{
                fieldObj.put("zdmc",zddm);
            }
            zdParams.put(KEY_YOBJ,fieldObj);
            LjqManager.insert(zdParams);
            oldFiledMap.put(zddm, null);
        }
        if(fieldsList.isEmpty()&&oldFiledMap.isEmpty()){
            return failed("没有查询到字段信息，请确认数据载体是否选择正确");
        }else{
            return success(jtdx.getDxmc()+"导入字段数："+count);
        }
    }

    /**
     * 清除数据对象相关缓存
     */
    protected void clearCache() {
        CacheFactory.clear(KEY_SJDX);
        CacheFactory.clear(KEY_FIELDS);
    }

    @Override
    public void destroy() throws Exception {
        log.debug("数据对象拦截器实例-释放");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.debug("数据对象拦截器实例-初始化");
    }
}
